Opi toteuttamaan tyyppiturvallisuus Fetch API:n avulla TypeScriptissä vankempia verkkosovelluksia varten. Tutustu parhaisiin käytäntöihin ja käytännön esimerkkeihin.
TypeScript Web API: Tyyppiturvallisuuden saavuttaminen Fetch-rajapinnalla vankkoihin sovelluksiin
Modernissa web-kehityksessä datan noutaminen API:sta on perustavanlaatuinen tehtävä. Vaikka JavaScriptin natiivi Fetch API tarjoaa kätevän tavan tehdä verkkopyyntöjä, siitä puuttuu sisäsyntyinen tyyppiturvallisuus. Tämä voi johtaa ajonaikaisiin virheisiin ja tehdä monimutkaisten sovellusten ylläpidosta haastavaa. TypeScript staattisen tyypityksen ominaisuuksineen tarjoaa tehokkaan ratkaisun tähän ongelmaan. Tämä kattava opas tutkii, miten toteuttaa tyyppiturvallisuus Fetch API:n kanssa TypeScriptissä, luoden vankempia ja ylläpidettävämpiä verkkosovelluksia.
Miksi tyyppiturvallisuus on tärkeää Fetch API:n kanssa
Ennen kuin sukellamme toteutuksen yksityiskohtiin, ymmärretään miksi tyyppiturvallisuus on ratkaisevan tärkeää Fetch API:n kanssa työskennellessä:
- Vähentyneet ajonaikaiset virheet: TypeScriptin staattinen tyypitys auttaa havaitsemaan virheet kehityksen aikana, estäen odottamattomia ajonaikaisia ongelmia, jotka johtuvat virheellisistä datatyypeistä.
- Parempi koodin ylläpidettävyys: Tyyppimerkinnät tekevät koodista helpommin ymmärrettävän ja ylläpidettävän, erityisesti suurissa projekteissa, joissa on useita kehittäjiä.
- Parempi kehityskokemus: IDE:t tarjoavat paremman automaattisen täydennyksen, virheiden korostuksen ja refaktorointikyvyn, kun tyyppitiedot ovat saatavilla.
- Datan validointi: Tyyppiturvallisuus mahdollistaa API:sta vastaanotetun datan rakenteen ja tyyppien validoinnin, varmistaen datan eheyden.
Fetch API:n peruskäyttö TypeScriptillä
Aloitetaan perusesimerkillä Fetch API:n käytöstä TypeScriptissä ilman tyyppiturvallisuutta:
async function fetchData(url: string) {
const response = await fetch(url);
const data = await response.json();
return data;
}
fetchData('https://api.example.com/users')
.then(data => {
console.log(data.name); // Mahdollinen ajonaikainen virhe, jos 'name' ei ole olemassa
});
Tässä esimerkissä `fetchData`-funktio hakee dataa annetusta URL-osoitteesta ja jäsentää vastauksen JSON-muodossa. Kuitenkin `data`-muuttujan tyyppi on implisiittisesti `any`, mikä tarkoittaa, että TypeScript ei suorita tyyppitarkistusta. Jos API-vastaus ei sisällä `name`-ominaisuutta, koodi heittää ajonaikaisen virheen.
Tyyppiturvallisuuden toteuttaminen rajapintojen avulla
Yleisin tapa lisätä tyyppiturvallisuutta Fetch API -kutsuille TypeScriptissä on määritellä rajapintoja, jotka edustavat odotetun datan rakennetta.
Rajapintojen määrittely
Oletetaan, että haemme käyttäjien luetteloa API:sta, joka palauttaa datan seuraavassa muodossa:
[
{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com"
},
{
"id": 2,
"name": "Jane Smith",
"email": "jane.smith@example.com"
}
]
Voimme määritellä rajapinnan edustamaan tätä datarakennetta:
interface User {
id: number;
name: string;
email: string;
}
Rajapintojen käyttö Fetch API:n kanssa
Nyt voimme päivittää `fetchData`-funktion käyttämään `User`-rajapintaa:
async function fetchData(url: string): Promise {
const response = await fetch(url);
const data = await response.json();
return data as User[];
}
fetchData('https://api.example.com/users')
.then(users => {
users.forEach(user => {
console.log(user.name); // Tyyppiturvallinen pääsy 'name'-ominaisuuteen
});
});
Tässä päivitetyssä esimerkissä olemme lisänneet tyyppimerkinnän `fetchData`-funktioon, joka määrittää sen palauttavan `Promise`-olion, joka ratkeaa `User`-objektien taulukoksi (`Promise
Tärkeä huomautus: Vaikka `as`-avainsana suorittaa tyyppiväitteen, se ei suorita ajonaikaista validointia. Se kertoo kääntäjälle mitä odottaa, mutta se ei takaa, että data todella vastaa väitettyä tyyppiä. Tässä vaiheessa kirjastot, kuten `io-ts` tai `zod`, ovat hyödyllisiä ajonaikaiseen validointiin, kuten keskustelemme myöhemmin.
Generiikoiden hyödyntäminen uudelleenkäytettävissä Fetch-funktioissa
Jotta voimme luoda uudelleenkäytettävämpiä hakuja tekeviä funktioita, voimme käyttää generiikoita. Generiikat antavat meille mahdollisuuden määritellä funktion, joka voi toimia eri datatyyppien kanssa ilman, että joudumme kirjoittamaan erillisiä funktioita kullekin tyypille.
Geneerisen Fetch-funktion määrittely
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: T = await response.json();
return data;
}
Tässä esimerkissä olemme määritelleet geneerisen `fetchData`-funktion, joka ottaa tyyppiparametrin `T`. Funktio palauttaa `Promise`-olion, joka ratkeaa tyyppiä `T` olevaan arvoon. Olemme myös lisänneet virheenkäsittelyn tarkistaaksemme, oliko vastaus onnistunut.
Geneerisen Fetch-funktion käyttö
Nyt voimme käyttää geneeristä `fetchData`-funktiota eri rajapintojen kanssa:
interface Post {
id: number;
title: string;
body: string;
userId: number;
}
fetchData<Post>('https://jsonplaceholder.typicode.com/posts/1')
.then(post => {
console.log(post.title); // Tyyppiturvallinen pääsy 'title'-ominaisuuteen
})
.catch(error => {
console.error("Error fetching post:", error);
});
fetchData<User[]>('https://api.example.com/users')
.then(users => {
users.forEach(user => {
console.log(user.email);
});
})
.catch(error => {
console.error("Error fetching users:", error);
});
Tässä esimerkissä käytämme geneeristä `fetchData`-funktiota hakemaan sekä yksittäisen `Post`-objektin että joukon `User`-objekteja. TypeScript päättelee automaattisesti oikean tyypin antamamme tyyppiparametrin perusteella.
Virheiden ja tilakoodien käsittely
On ratkaisevan tärkeää käsitellä virheitä ja tilakoodeja työskenneltäessä Fetch API:n kanssa. Voimme lisätä virheenkäsittelyn `fetchData`-funktioomme tarkistaaksemme HTTP-virheet ja heittääksemme virheen tarvittaessa.
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: T = await response.json();
return data;
}
Tässä päivitetyssä esimerkissä tarkistamme `response.ok`-ominaisuutta, joka osoittaa, onko vastauksen tilakoodi alueella 200-299. Jos vastaus ei ole OK, heitämme virheen tilakoodin kanssa.
Ajonaikainen datan validointi `io-ts`:n tai `zod`:n avulla
Kuten aiemmin mainittiin, TypeScriptin tyyppiväitteet (`as`) eivät suorita ajonaikaista validointia. Varmistaaksemme, että API:sta vastaanotettu data todella vastaa odotettua tyyppiä, voimme käyttää kirjastoja kuten `io-ts` tai `zod`.
`io-ts`:n käyttö
`io-ts` on kirjasto ajonaikaisten tyyppien määrittelyyn ja datan validoimiseen näitä tyyppejä vastaan.
import * as t from 'io-ts'
import { PathReporter } from 'io-ts/PathReporter'
const UserType = t.type({
id: t.number,
name: t.string,
email: t.string
})
type User = t.TypeOf<typeof UserType>
async function fetchDataAndValidate(url: string): Promise<User[]> {
const response = await fetch(url)
const data = await response.json()
const decodedData = t.array(UserType).decode(data)
if (decodedData._tag === 'Left') {
const errors = PathReporter.report(decodedData)
throw new Error(`Validation errors: ${errors.join('\n')}`)
}
return decodedData.right
}
fetchDataAndValidate('https://api.example.com/users')
.then(users => {
users.forEach(user => {
console.log(user.name);
});
})
.catch(error => {
console.error('Error fetching and validating users:', error);
});
Tässä esimerkissä määrittelemme `UserType`-tyypin käyttäen `io-ts`:ää, joka vastaa `User`-rajapintaamme. Käytämme sitten `decode`-metodia API:sta vastaanotetun datan validoimiseen. Jos validointi epäonnistuu, heitämme virheen validointivirheiden kanssa.
`zod`:n käyttö
`zod` on toinen suosittu kirjasto skeemojen määrittelyyn ja validointiin.
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
});
type User = z.infer<typeof UserSchema>;
async function fetchDataAndValidate(url: string): Promise<User[]> {
const response = await fetch(url);
const data = await response.json();
const parsedData = z.array(UserSchema).safeParse(data);
if (!parsedData.success) {
throw new Error(`Validation errors: ${parsedData.error.message}`);
}
return parsedData.data;
}
fetchDataAndValidate('https://api.example.com/users')
.then(users => {
users.forEach(user => {
console.log(user.name);
});
})
.catch(error => {
console.error('Error fetching and validating users:', error);
});
Tässä esimerkissä määrittelemme `UserSchema`-skeeman käyttäen `zod`:a, joka vastaa `User`-rajapintaamme. Käytämme sitten `safeParse`-metodia API:sta vastaanotetun datan validoimiseen. Jos validointi epäonnistuu, heitämme virheen validointivirheiden kanssa.
Sekä `io-ts` että `zod` tarjoavat tehokkaan tavan varmistaa, että API:sta vastaanotettu data vastaa odotettua tyyppiä ajonaikaisesti.
Integrointi suosittuihin Frameworkeihin (React, Angular, Vue.js)
Tyyppiturvalliset Fetch API -kutsut voidaan helposti integroida suosittuihin JavaScript-frameworkeihin, kuten Reactiin, Angulariin ja Vue.js:ään.
React-esimerkki
import React, { useState, useEffect } from 'react';
interface User {
id: number;
name: string;
email: string;
}
function UserList() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchUsers() {
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data: User[] = await response.json();
setUsers(data);
} catch (error: any) {
setError(error.message);
} finally {
setLoading(false);
}
}
fetchUsers();
}, []);
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Error: {error}</p>;
}
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UserList;
Tässä React-esimerkissä käytämme `useState`-hookia `users`-taulukon tilan hallintaan. Käytämme myös `useEffect`-hookia käyttäjien noutamiseen API:sta komponentin latautuessa. Olemme lisänneet tyyppimerkinnät `users`-tilaan ja `data`-muuttujaan tyyppiturvallisuuden varmistamiseksi.
Angular-esimerkki
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
interface User {
id: number;
name: string;
email: string;
}
@Component({
selector: 'app-user-list',
template: `
<ul>
<li *ngFor="let user of users">{{ user.name }}</li>
</ul>
`,
styleUrls: []
})
export class UserListComponent implements OnInit {
users: User[] = [];
constructor(private http: HttpClient) { }
ngOnInit() {
this.http.get<User[]>('https://api.example.com/users')
.subscribe(users => {
this.users = users;
});
}
}
Tässä Angular-esimerkissä käytämme `HttpClient`-palvelua API-kutsun tekemiseen. Määritämme vastauksen tyypiksi `User[]` generiikkojen avulla, mikä varmistaa tyyppiturvallisuuden.
Vue.js-esimerkki
<template>
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
</template>
<script>
import { defineComponent, ref, onMounted } from 'vue'
interface User {
id: number
name: string
email: string
}
export default defineComponent({
setup() {
const users = ref<User[]>([])
onMounted(async () => {
try {
const response = await fetch('https://api.example.com/users')
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const data: User[] = await response.json()
users.value = data
} catch (error) {
console.error('Error fetching users:', error)
}
})
return {
users
}
}
})
</script>
Tässä Vue.js-esimerkissä käytämme `ref`-funktiota reaktiivisen `users`-taulukon luomiseen. Käytämme `onMounted`-elinkaarihookia käyttäjien noutamiseen API:sta, kun komponentti on asennettu. Olemme lisänneet tyyppimerkinnät `users`-refiin ja `data`-muuttujaan tyyppiturvallisuuden varmistamiseksi.
Parhaat käytännöt tyyppiturvallisiin Fetch API -kutsuihin
Seuraavassa on joitakin parhaita käytäntöjä, joita kannattaa noudattaa toteuttaessasi tyyppiturvallisia Fetch API -kutsuja TypeScriptissä:
- Määrittele rajapinnat: Määrittele aina rajapinnat, jotka edustavat odotetun datan rakennetta.
- Käytä generiikoita: Käytä generiikoita luodaksesi uudelleenkäytettäviä hakufunktioita, jotka voivat toimia eri datatyyppien kanssa.
- Käsittele virheet: Toteuta virheenkäsittely HTTP-virheiden tarkistamiseksi ja virheiden heittämiseksi tarvittaessa.
- Validoi data: Käytä kirjastoja kuten `io-ts` tai `zod` API:sta vastaanotetun datan validoimiseen ajonaikaisesti.
- Tyypitä tilasi: Kun integroit frameworkeihin, kuten Reactiin, Angulariin ja Vue.js:ään, tyypitä tilamuuttujasi ja API-vastauksesi.
- Keskitä API-määritykset: Luo keskitetty sijainti API-perus-URL:lle ja mahdollisille yleisille otsakkeille tai parametreille. Tämä helpottaa API-määritysten ylläpitoa ja päivittämistä. Harkitse ympäristömuuttujien käyttöä eri ympäristöissä (kehitys, testaus, tuotanto).
- Käytä API-asiakaskirjastoa (valinnainen): Harkitse API-asiakaskirjaston, kuten Axiosin tai OpenAPI/Swagger-määrityksestä generoidun asiakkaan, käyttöä. Nämä kirjastot tarjoavat usein sisäänrakennettuja tyyppiturvallisuusominaisuuksia ja voivat yksinkertaistaa API-vuorovaikutusta.
Yhteenveto
Tyyppiturvallisuuden toteuttaminen Fetch API:n kanssa TypeScriptissä on välttämätöntä vankkojen ja ylläpidettävien verkkosovellusten rakentamisessa. Määrittämällä rajapintoja, käyttämällä generiikoita, käsittelemällä virheitä ja validoimalla dataa ajonaikaisesti, voit vähentää merkittävästi ajonaikaisia virheitä ja parantaa yleistä kehittäjäkokemusta. Tämä opas tarjoaa kattavan yleiskuvan tyyppiturvallisuuden saavuttamisesta Fetch API:n kanssa, sekä käytännön esimerkkejä ja parhaita käytäntöjä. Noudattamalla näitä ohjeita voit luoda luotettavampia ja skaalautuvampia verkkosovelluksia, jotka ovat helpommin ymmärrettäviä ja ylläpidettäviä.
Lisää tutkittavaa
- OpenAPI/Swagger-koodin generointi: Tutustu työkaluihin, jotka generoivat automaattisesti TypeScript API-asiakkaita OpenAPI/Swagger-määrityksistä. Tämä voi suuresti yksinkertaistaa API-integraatiota ja varmistaa tyyppiturvallisuuden. Esimerkkejä ovat: `openapi-typescript` ja `swagger-codegen`.
- GraphQL ja TypeScript: Harkitse GraphQL:n käyttöä TypeScriptin kanssa. GraphQL:n vahvasti tyypitetty skeema tarjoaa erinomaisen tyyppiturvallisuuden ja eliminoi datan ylinoudon.
- Tyyppiturvallisuuden testaus: Kirjoita yksikkötestejä varmistaaksesi, että API-kutsusi palauttavat odotetun tyyppistä dataa. Tämä auttaa varmistamaan, että tyyppiturvallisuusmekanismisi toimivat oikein.